<?php
/**
 * WDS GROUP
 *
 * @name        Debug.php
 * @category    WDS
 * @package     Controller/Plugin   
 * @subpackage  
 * @author      Thuy Dinh Xuan <thuydx@wds.vn>
 * @copyright   Copyright (c)2008-2010 WDS GROUP. All rights reserved
 * @license     http://wds.vn/license/     WDS Software License
 * @version     $1.0$
 * 8:35:23 PM - 2011
 *
 * LICENSE
 *
 * This source file is copyrighted by WDS GROUP, full details in LICENSE.txt.
 * It is also available through the Internet at this URL:
 * http://wds.vn/license/
 * If you did not receive a copy of the license and are unable to
 * obtain it through the Internet, please send an email
 * to license@wds.vn so we can send you a copy immediately.
 *
 */

/**
 * @see Zend_Controller_Exception
 */
require_once 'Zend/Controller/Exception.php';

/**
 * @see Zend_Version
 */
require_once 'Zend/Version.php';

/**
 * @see WDS_Controller_Plugin_Debug_Plugin_Text
 */
require_once 'WDS/Controller/Plugin/Debug/Plugin/Text.php';

/**
 * WDS GROUP
 *
 * @name        Debug.php
 * @category    WDS
 * @package     Controller/Plugin   
 * @subpackage  
 * @author      Thuy Dinh Xuan <thuydx@wds.vn>
 * @copyright   Copyright (c)2008-2010 WDS GROUP. All rights reserved
 * @license     http://wds.vn/license/     WDS Software License
 * @version     $1.0$
 * 8:35:23 PM - 2011
 *
 * LICENSE
 *
 * This source file is copyrighted by WDS GROUP, full details in LICENSE.txt.
 * It is also available through the Internet at this URL:
 * http://wds.vn/license/
 * If you did not receive a copy of the license and are unable to
 * obtain it through the Internet, please send an email
 * to license@wds.vn so we can send you a copy immediately.
 *
 */
class WDS_Controller_Plugin_Debug extends Zend_Controller_Plugin_Abstract
{
    /**
     * Contains registered plugins
     *
     * @var array
     */
    protected $_plugins = array();

    /**
     * Contains options to change Debug Bar behavior
     */
    protected $_options = array(
        'plugins'           => array(
            'Variables' => null,
            'Time' => null,
            'Memory' => null,
    		'Doctrine' => array()),
        'image_path'        => null
    );
    
    /**
     * Standard plugins
     *
     * @var array
     */
    public static $standardPlugins = array(
        'Cache', 
        'Html', 
        'Database', 
        'Exception', 
        'File', 
        'Memory', 
        'Registry', 
        'Time', 
        'Variables',
        'Log',
        'Doctrine'
        );

    /**
     * Debug Bar Version Number
     * for internal use only
     *
     * @var string
     */
    protected $_version = '1.0.1';

    /**
     * Creates a new instance of the Debug Bar
     *
     * @param array|Zend_Config $options
     * @throws Zend_Controller_Exception
     * @return void
     */

    protected $_closingBracket = null;

    public function __construct($options = null)
    {
        if (isset($options)) {
            if ($options instanceof Zend_Config) {
                $options = $options->toArray();
            }

            /*
             * Verify that adapter parameters are in an array.
             */
            if (!is_array($options)) {
                throw new Zend_Exception('Debug parameters must be in an array or a Zend_Config object');
            }
            $this->setOptions($options);
        }
        
        /**
         * Creating ZF Version Tab always shown
         */
        $version = new WDS_Controller_Plugin_Debug_Plugin_Text();
        $version->setPanel($this->_getVersionPanel())
                ->setTab($this->_getVersionTab())
                ->setIdentifier('copyright')
                ->setIconData('');
                // ->setIconData('');
        $this->registerPlugin($version);

        /**
         * Creating the log tab
         */
        $logger = new WDS_Controller_Plugin_Debug_Plugin_Log();
        $this->registerPlugin($logger);
        $logger->mark('Startup - Debug construct()', true);
        
        /**
         * Loading already defined plugins
         */
        $this->_loadPlugins();
    }
    
    /**
     * Get the Debug logger
     *
     * @return Zend_Log
     */
    public function getLogger()
    {
        return $this->getPlugin('Log')->logger();
    }

    /**
     * Sets options of the Debug Bar
     *
     * @param array $options
     * @return WDS_Controller_Plugin_Debug
     */
    public function setOptions(array $options = array())
    {
        if (isset($options['image_path'])) {
            $this->_options['image_path'] = $options['image_path'];
        }
        
        if (isset($options['plugins'])) {
            $this->_options['plugins'] = $options['plugins'];
        }
        return $this;
    }

    /**
     * Register a new plugin in the Debug Bar
     *
     * @param WDS_Controller_Plugin_Debug_Plugin_Interface
     * @return WDS_Controller_Plugin_Debug
     */
    public function registerPlugin(WDS_Controller_Plugin_Debug_Plugin_Interface $plugin)
    {
        $this->_plugins[$plugin->getIdentifier()] = $plugin;
        return $this;
    }

    /**
     * Unregister a plugin in the Debug Bar
     *
     * @param string $plugin
     * @return WDS_Controller_Plugin_Debug
     */
    public function unregisterPlugin($plugin)
    {
        if (false !== strpos($plugin, '_')) {
            foreach ($this->_plugins as $key => $_plugin) {
                if ($plugin == get_class($_plugin)) {
                    unset($this->_plugins[$key]);
                }
            }
        } else {
            $plugin = strtolower($plugin);
            if (isset($this->_plugins[$plugin])) {
                unset($this->_plugins[$plugin]);
            }
        }
        return $this;
    }
    
    /**
     * Get a registered plugin in the Debug Bar
     *
     * @param string $identifier
     * @return WDS_Controller_Plugin_Debug_Plugin_Interface
     */
    public function getPlugin($identifier)
    {
        $identifier = strtolower($identifier);
        if (isset($this->_plugins[$identifier])) {
            return $this->_plugins[$identifier];
        }
        return false;
    }
    
    /**
     * Defined by Zend_Controller_Plugin_Abstract
     */
    public function dispatchLoopShutdown()
    {
        if ($this->getRequest()->isXmlHttpRequest()) {
            return;
        }
        $disable = Zend_Controller_Front::getInstance()->getRequest()->getParam('DEBUG_DISABLE');
        if (isset($disable)) {
            return;
        }
        
        $html = '';

        $html .= "<div id='ZFDebug_info'>\n";
        $html .= "\t<span class='ZFDebug_span' style='padding-right:0px;' onclick='DebugPanel(DebugCurrent);'>
            <img style='vertical-align:middle;' src='".$this->_icon('close')."'>&nbsp;
        </span>\n";

        /**
         * Creating panel content for all registered plugins
         */
        foreach ($this->_plugins as $plugin) {
            $tab = $plugin->getTab();
            if ($tab == '') {
                continue;
            }

            if (null !== $this->_options['image_path'] && 
                file_exists($this->_options['image_path'] .'/'. $plugin->getIdentifier() .'.png')) {
                
                $pluginIcon = $this->_options['image_path'] .'/'. $plugin->getIdentifier() .'.png';
            } else {
                $pluginIcon = $plugin->getIconData();
            }

            /* @var $plugin WDS_Controller_Plugin_Debug_Plugin_Interface */
            $showPanel = ($plugin->getPanel() == '') ? 'log' : $plugin->getIdentifier();
            $html .= "\t".'<span id="ZFDebugInfo_'.$plugin->getIdentifier()
                   . '" class="ZFDebug_span clickable" onclick="DebugPanel(\'ZFDebug_' 
                   . $showPanel . '\');">' . "\n";
            if ($pluginIcon) {
                $html .= "\t\t".'<img src="' . $pluginIcon . '" style="vertical-align:middle" alt="' 
                       . $plugin->getIdentifier() . '" title="' 
                       . $plugin->getIdentifier() . '"> ' . "\n";
            }
            $html .= $tab . "</span>\n";
        }
        
        $html .= '<span id="ZFDebugInfo_Request" class="ZFDebug_span">
        			<img style="float: left; height: 16px; margin: 10px 5px 0px 0px;" src="" alt="" />
        		'
               . "\n"
               . round(memory_get_peak_usage()/1024) . 'K of '.ini_get("memory_limit").' in '
               . round((microtime(true)-$_SERVER['REQUEST_TIME'])*1000) . 'ms'
               . '</span>' . "\n";

        $html .= "</div>\n";
        $html .= '<div id="DebugResize"></div>';

        /**
         * Creating menu tab for all registered plugins
         */
        $this->getPlugin('log')->mark('Shutdown', true);
        foreach ($this->_plugins as $plugin) {
            $panel = $plugin->getPanel();
            if ($panel == '') {
                continue;
            }

            /* @var $plugin WDS_Controller_Plugin_Debug_Plugin_Interface */
            $html .= "\n" . '<div id="ZFDebug_' . $plugin->getIdentifier()
                  . '" class="ZFDebug_panel" name="ZFDebug_panel">' . "\n" . $panel . "\n</div>\n";
        }

        $this->_output($html);
    }

    ### INTERNAL METHODS BELOW ###

    /**
     * Load plugins set in config option
     *
     * @return void;
     */
    protected function _loadPlugins()
    {
        foreach ($this->_options['plugins'] as $plugin => $options) {
            if (is_numeric($plugin)) {
                # Plugin passed as array value instead of key
                $plugin = $options;
                $options = array();
            }
            // Register an instance
            if (is_object($plugin) && in_array('WDS_Controller_Plugin_Debug_Plugin_Interface', class_implements($plugin))) {
                $this->registerPlugin($plugin);
                continue;
            }
            
            if (!is_string($plugin)) {
                throw new Exception("Invalid plugin name", 1);
            }
            $plugin = ucfirst($plugin);
            
            // Register a classname
            if (in_array($plugin, WDS_Controller_Plugin_Debug::$standardPlugins)) {
                // standard plugin
                $pluginClass = 'WDS_Controller_Plugin_Debug_Plugin_' . $plugin;
            } else {
                // we use a custom plugin
                if (!preg_match('~^[\w]+$~D', $plugin)) {
                    throw new Zend_Exception("Debug: Invalid plugin name [$plugin]");
                }
                $pluginClass = $plugin;
            }

            require_once str_replace('_', DIRECTORY_SEPARATOR, $pluginClass) . '.php';
            $object = new $pluginClass($options);
            $this->registerPlugin($object);
        }
    }

    /**
     * Return version tab
     *
     * @return string
     */
    protected function _getVersionTab()
    {
        return '<strong>WDS Debug</strong>';
        // return ' ' . Zend_Version::VERSION . '/'.phpversion();
    }

    /**
     * Returns version panel
     *
     * @return string
     */
    protected function _getVersionPanel()
    {
        $panel = "<h4>WDS Debug $this->_version – Zend Framework " 
               . Zend_Version::VERSION . " on PHP " . phpversion() . "</h4>\n"
               . "<p>Copyright © 2008-1011 <a href='http://wds.vn'>WDS GROUP</a>" . $this->getLinebreak()
               . "<p>Disable Debug temporarily by sending DEBUG_DISABLE as a GET/POST parameter</p>";
        $panel .= '<h4>Zend Framework '.Zend_Version::VERSION.' / PHP '.phpversion().' with extensions:</h4>';
        $extensions = get_loaded_extensions();
        natcasesort($extensions);
        $panel .= implode('<br>', $extensions);
        return $panel;
    }

    /**
     * Returns path to the specific icon
     *
     * @return string
     */
    protected function _icon($kind)
    {
        switch ($kind) {
            case 'database':
                if (null === $this->_options['image_path'])
                    return '';

                return $this->_options['image_path'] . '/database.png';
                break;
            case 'exception':
                if (null === $this->_options['image_path'])
                    return '';

                return $this->_options['image_path'] . '/exception.png';
                break;
            case 'error':
                if (null === $this->_options['image_path'])
                    return '';

                return $this->_options['image_path'] . '/error.png';
                break;
            case 'close':
                return '';
                break;
            case 'memory':
                return '';
                break;    
            default:
                if (null === $this->_options['image_path'])
                    return '';

                return $this->_options['image_path'] . '/unknown.png';
                break;
        }
    }

    /**
     * Returns html header for the Debug Bar
     *
     * @return string
     */
    protected function _headerOutput() 
    {
        $collapsed = isset($_COOKIE['DebugCollapsed']) ? $_COOKIE['DebugCollapsed'] : '';
        if ($collapsed) {
            $boxheight = isset($_COOKIE['DebugHeight']) ? $_COOKIE['DebugHeight'] : '240';
        } else {
            $boxheight = '32';
        }
        return ('
    <style type="text/css" media="screen">
        html,body {height:100%}
        #ZFDebug, #ZFDebug div, #ZFDebug span, #ZFDebug h1, #ZFDebug h2, #ZFDebug h3, #ZFDebug h4, #ZFDebug h5, #ZFDebug h6, #ZFDebug p, #ZFDebug blockquote, #ZFDebug pre, #ZFDebug a, #ZFDebug code, #ZFDebug em, #ZFDebug img, #ZFDebug strong, #ZFDebug dl, #ZFDebug dt, #ZFDebug dd, #ZFDebug ol, #ZFDebug ul, #ZFDebug li, #ZFDebug table, #ZFDebug tbody, #ZFDebug tfoot, #ZFDebug thead, #ZFDebug tr, #ZFDebug th, #ZFDebug td {
        	margin: 0;
        	padding: 0;
        	border: 0;
        	outline: 0;
        	font-size: 100%;
        	vertical-align: baseline;
        	background: transparent;
        	color: #999999;
        	}
        
        #ZFDebug_offset {height:'.$boxheight.'px}
        #ZFDebug {height:'.$boxheight.'px; width:100%; background:#262626; 
                        font: 12px/1.4em Lucida Grande, Lucida Sans Unicode, sans-serif; 
                        position:fixed; bottom:0px; left:0px; color:#FFF; background:#000000;
                        z-index:2718281828459045;}
        #ZFDebug p {margin:1em 0}
        #ZFDebug a {color:#FFFFFF}
        #ZFDebug tr {color:#FFFFFF;}
        #ZFDebug td {vertical-align:top; padding-bottom:1em}
        #ZFDebug ol {margin:1em 0 0 0; padding:0; list-style-position: inside;}
        #ZFDebug li {margin:0;}
        #ZFDebug .clickable {cursor:pointer}
        #ZFDebug #ZFDebug_info {display:block; height:32px;
                       background:url() }
        #ZFDebug #DebugResize {cursor:row-resize; height:1px; border-top:1px solid #1a1a1a;border-bottom:1px solid #333333; }
        #ZFDebug .ZFDebug_span {padding:0 15px; line-height:32px; display:block; float:left}
        #ZFDebug .ZFDebug_panel {padding:0px 15px 15px 15px;
                        font: 11px/1.4em Menlo, Monaco, Lucida Console, monospace;
                        text-align:left; height:'.($boxheight-50).'px; overflow:auto; display:none; }
        #ZFDebug h4 {font:bold 12px/1.4em Menlo, Monaco, Lucida Console, monospace; margin:1em 0;}
        #ZFDebug .ZFDebug_active {background:#1a1a1a;}
        #ZFDebug .ZFDebug_panel .pre {margin:0 0 0 22px}
        #ZFDebug_exception { border:1px solid #CD0A0A;display: block; }
    </style>
    <script type="text/javascript">
        var DebugLoad = window.onload;
        window.onload = function(){
            if (DebugLoad) {
                DebugLoad();
            }
            if ("'.$collapsed.'" != "") {
                DebugPanel("' . $collapsed . '");
            }
            window.debugHeight = "'.(isset($_COOKIE['DebugHeight']) ? $_COOKIE['DebugHeight'] : '240').'";
            
            document.onmousemove = function(e) {
                var event = e || window.event;
                window.debugMouse = Math.max(40, Math.min(window.innerHeight, -1*(event.clientY-window.innerHeight-32)));
            }
            
            var DebugResizeTimer = null;
            document.getElementById("DebugResize").onmousedown=function(e){
                DebugResize();
                DebugResizeTimer = setInterval("DebugResize()",50);
                return false;
            }
            document.onmouseup=function(e){
                clearTimeout(DebugResizeTimer);
            }
        };
        
        function DebugResize()
        {
            window.debugHeight = window.debugMouse;
            document.cookie = "DebugHeight="+window.debugHeight+";expires=;path=/";
            document.getElementById("ZFDebug").style.height = window.debugHeight+"px";
            document.getElementById("ZFDebug_offset").style.height = window.debugHeight+"px";
            
            var panels = document.getElementById("ZFDebug").children;
            for (var i=0; i < document.getElementById("ZFDebug").childElementCount; i++) {
                if (panels[i].className.indexOf("ZFDebug_panel") == -1)
                    continue;
                
                panels[i].style.height = window.debugHeight-50+"px";
            }
        }
    
        var DebugCurrent = null;
    
        function DebugPanel(name) {
            if (DebugCurrent == name) {
                document.getElementById("ZFDebug").style.height = "32px";
                document.getElementById("ZFDebug_offset").style.height = "32px";
                DebugCurrent = null;
                document.cookie = "DebugCollapsed=;expires=;path=/";
            } else {
                document.getElementById("ZFDebug").style.height = window.debugHeight+"px";
                document.getElementById("ZFDebug_offset").style.height = window.debugHeight+"px";
                DebugCurrent = name;
                document.cookie = "DebugCollapsed="+name+";expires=;path=/";
            }

            var panels = document.getElementById("ZFDebug").children;
            for (var i=0; i < document.getElementById("ZFDebug").childElementCount; i++) {
                if (panels[i].className.indexOf("ZFDebug_panel") == -1)
                    continue;
                
                if (DebugCurrent && panels[i].id == name) {
                    document.getElementById("ZFDebugInfo_"+name.substring(8)).className += " ZFDebug_active";
                    panels[i].style.display = "block";
                    panels[i].style.height = (window.debugHeight-50)+"px";
                } else {
                    var element = document.getElementById("ZFDebugInfo_"+panels[i].id.substring(8));
                    element.className = element.className.replace("ZFDebug_active", "");
                    panels[i].style.display = "none";
                }
            }
        }
    </script>
    ');
    }

    /**
     * Appends Debug Bar html output to the original page
     *
     * @param string $html
     * @return void
     */
    protected function _output($html)
    {
        $html = "<div id='ZFDebug_offset'></div>\n<div id='ZFDebug'>\n$html\n</div>\n</body>";
        $response = $this->getResponse();
        // $response->setBody(preg_replace('/(<\/head>)/i', $this->_headerOutput() . '$1', $response->getBody()));
        $response->setBody(str_ireplace('</body>', $this->_headerOutput() . $html, $response->getBody()));
    }
    
    public function getLinebreak()
    {
        return '<br'.$this->getClosingBracket();
    }

    public function getClosingBracket()
    {
        if (!$this->_closingBracket) {
            if ($this->_isXhtml()) {
                $this->_closingBracket = ' />';
            } else {
                $this->_closingBracket = '>';
            }
        }

        return $this->_closingBracket;
    }  
    
    protected function _isXhtml()
    {
        if ($view = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer')->view) {
            $doctype = $view->doctype();
            return $doctype->isXhtml();
        }
        return false;
    }
}